#include "Audio.h"
#include "Sound.h"

#include "ComponentManager.h"
#include <iostream>
using namespace std;


//structure that represent a playing sounds
struct playingSound
{
	int active; // 1 if this sound should be played
	CSound* sound; // sound data to play
	Uint32 position; // current position in the sound buffer	
};

//number max of playing sounds
const int m_maxPlayingSounds = 10;

int CAudio::m_volume = SDL_MIX_MAXVOLUME / 2;

//array of playing sounds
playingSound m_arrayOfPlayingSound[m_maxPlayingSounds];

CAudio::CAudio(void):m_numberOfSoundLoaded(0), m_audioSpec(0)//, m_maxVolume(SDL_MIX_MAXVOLUME)
{
	for(int i=0; i<m_maxPlayingSounds; i++)
	{
		m_arrayOfPlayingSound[i].active = 0;
	}
	for(int i=0; i<MAX_LOADED_SOUNDS; i++)
	{
		m_arrayOfSound[i]=0;
	}
}
CAudio::~CAudio(void)
{
	for (int i=0; i<m_maxPlayingSounds; i++)
	{
		if(m_arrayOfSound[i]!=0)
		{
			delete m_arrayOfSound[i];
			m_arrayOfSound[i]=0;
		}
	}
	if(m_audioSpec!=0)
		free(m_audioSpec);
}

bool CAudio::init(void)
{
	//the audio specification that we want
	SDL_AudioSpec* desiredAudioSpec;

	//allocate the desiredAudioSpec and the m_audioSpec
	m_audioSpec = (SDL_AudioSpec*)malloc(sizeof(SDL_AudioSpec));
	if(m_audioSpec==0)
		throw "Audio buffer allocated failed";

	desiredAudioSpec=(SDL_AudioSpec*)malloc(sizeof(SDL_AudioSpec));
	if(desiredAudioSpec==0)
		throw "Desired audio buffer allocated failed";

	//set the desired audio spec
	desiredAudioSpec->freq = 44100;
	desiredAudioSpec->format = AUDIO_S16;
	desiredAudioSpec->samples = 1024;
	desiredAudioSpec->channels = 2;
	desiredAudioSpec->callback = audioCallback;
	desiredAudioSpec->userdata = 0;

	//initialise sdl audio
	if(-1== SDL_OpenAudio(desiredAudioSpec, m_audioSpec))
		throw "SDL failed to open audio stream";

	//free the desired audio spec
	free(desiredAudioSpec);

	return true;
}


bool CAudio::loadSound(int* idSound, string path)
{
	if(m_numberOfSoundLoaded<MAX_LOADED_SOUNDS)
	{
		CSound* newSound = new CSound();
		if(newSound->loadAndConvertSound(path.c_str(), m_audioSpec))
		{
			m_arrayOfSound[m_numberOfSoundLoaded]=newSound;
			*idSound = m_numberOfSoundLoaded;
			m_numberOfSoundLoaded++;		
			return true;
		}
		else
			return false;
	}
	CM.LOGCONSOLE().Log()<<"LOG : AUDIO : ERROR the sound "<<path<<" can't be loaded because already "
		<<m_numberOfSoundLoaded<<" have been loaded.\n";
	return false;
}

bool CAudio::loadSoundFromMemory(int* idSound, const string& filename)
{
	char* buffer;
	int size;

	if(!CM.FILE().getPackedFile(filename, &buffer, &size))
	{
		CM.LOGCONSOLE().Log()<<"The file :"<<filename<<" is not in any pack file";
		return false;
	}

	if(m_numberOfSoundLoaded<MAX_LOADED_SOUNDS)
	{
		CSound* newSound = new CSound();
		if(newSound->loadAndConvertSoundFromMemory(buffer, size, m_audioSpec))
		{
			m_arrayOfSound[m_numberOfSoundLoaded]=newSound;
			*idSound = m_numberOfSoundLoaded;
			m_numberOfSoundLoaded++;

			//delete buffer;
			return true;
		}
		else
		{
			//delete buffer;
			return false;
		}
	}
	CM.LOGCONSOLE().Log()<<"LOG : AUDIO : ERROR the sound "<<filename<<" can't be loaded because already "
		<<m_numberOfSoundLoaded<<" have been loaded.\n";
	//delete buffer;
	return false;

}

int CAudio::playSound(int indexSound)
{
	int i;
	// Find an empty slot for this sound
	for (i = 0; i < m_maxPlayingSounds; ++i)
	{
		if (m_arrayOfPlayingSound[i].active == 0) break;
	}
	// Report failure if there are no slots
	if (i == m_maxPlayingSounds) return -1;

	// The 'playing' structures are accessed by the audio callback, so we should
	// obtain a lock before we access them
	SDL_LockAudio();
	m_arrayOfPlayingSound[i].active = 1;
	m_arrayOfPlayingSound[i].sound = m_arrayOfSound[indexSound];
	m_arrayOfPlayingSound[i].position = 0;
	SDL_UnlockAudio();
	SDL_PauseAudio(0);
	return 0;
}
void CAudio::clearPlayingSounds(void)
{
	for (int i = 0; i < m_maxPlayingSounds; ++i){}
		//m_arrayOfPlayingSound[i] = 0;
}

void CAudio::audioCallback(void * /*user_data*/, Uint8 *audio, int length)
{
	int i;
	// clear the audio buffer so we can mix samples into it
	memset(audio, 0, length);

	// mix in each sound
	for (i = 0; i < m_maxPlayingSounds; ++i) 
	{
		if (m_arrayOfPlayingSound[i].active != 0) 
		{
			Uint8* sound_buf;
			Uint32 sound_len;

			// Locate this sound's current buffer position
			sound_buf = m_arrayOfPlayingSound[i].sound->getSample();
			sound_buf += m_arrayOfPlayingSound[i].position;

			// Determine the number of samples to mix
			if ((m_arrayOfPlayingSound[i].position + length) > m_arrayOfPlayingSound[i].sound->getLength())
				sound_len = m_arrayOfPlayingSound[i].sound->getLength() - m_arrayOfPlayingSound[i].position;
			else
				sound_len = length;

			// Mix this sound into the stream
			SDL_MixAudio(audio, sound_buf, sound_len, m_volume);

			// Update the sound buffer's position
			m_arrayOfPlayingSound[i].position += length;

			// Have we reached the end of the sound?
			if (m_arrayOfPlayingSound[i].position >= m_arrayOfPlayingSound[i].sound->getLength())
				m_arrayOfPlayingSound[i].active = 0; // mark as inactive
		}
	}
}

void CAudio::setVolume(int volume)
{
	m_volume = volume;
	if(m_volume<0)
		m_volume=0;
}

int CAudio::getVolume(void)const
{
	return m_volume;
}
